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"
38 #include "vepgsettimer.h"
50 static const char* TAG = "VEpg";
52 VEpg* VEpg::instance = NULL;
54 VEpg::VEpg(MessageReceiver* tparent, u4 tcurrentChannelIndex, std::shared_ptr<ChannelList> tchanList)
57 currentChannelIndex = tcurrentChannelIndex;
59 // PAL / NTSC sizes -----------------------
62 //int summaryLines, summaryLowerPadding;
64 int fontHeight=getFontHeight() + 4;
65 //u4 gridRows; // is a class member
68 if (Video::getInstance()->getFormat() == Video::PAL)
85 //summaryLowerPadding = 28;
89 int screenwidthhalf=Video::getInstance()->getScreenWidth()/2;
90 int screenheighthalf=Video::getInstance()->getScreenHeight()/2;
91 // summaryLines = ((float)screenheighthalf)/((float)fontHeight))-1;
92 // summaryLowerPadding = screenheighthalf-summaryLines*(fontHeight);
93 gridRows = (screenheighthalf-fontHeight*3-50)/fontHeight;
96 // initialise variables and pointers
97 boxstack = BoxStack::getInstance();
100 chanList = tchanList;
103 eventLista.resize(gridRows);
104 for(u4 listIndex = 0; listIndex < gridRows; listIndex++)
106 // initialise array of pointers to eventlist structures
107 eventLista[listIndex] = NULL;
110 // Create pallet on which to paint our epg view and position it in centre of screen.
111 // Need to reduce size to deal with overscanning TVs.
113 setSize(Video::getInstance()->getScreenWidth(), Video::getInstance()->getScreenHeight());
118 // setBackgroundColour(DrawStyle::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 (u4 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;
186 MessageQueue::getInstance()->addReceiver(this);
189 void VEpg::preDelete()
191 Timers::getInstance()->cancelTimer(this, 1);
196 MessageQueue::getInstance()->removeReceiver(this);
200 for(u4 listIndex = 0; listIndex < gridRows; listIndex++)
202 if (eventLista[listIndex])
204 (eventLista)[listIndex]->clear();
205 delete eventLista[listIndex];
208 // delete [] eventLista; // FIXME
210 // destroy dynamically allocated memory
213 VEpg* VEpg::getInstance()
218 void VEpg::setInfo(Event* event)
221 struct tm btime; // to hold programme start and end time
222 char timeString[9]; // to hold programme start and end time
223 int length = strlen(event->title.c_str()); // calculate length of programme title string
224 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
225 time_t eventtime = event->time;
226 LOCALTIME_R(&eventtime, &btime); //get programme start time
228 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
230 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
232 strcpy(title, timeString); // put it in our buffer
233 t = event->time + event->duration; //get programme end time
234 LOCALTIME_R(&t, &btime);
236 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
238 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
240 strcat(title, timeString); // put it in our buffer
242 strcat(title, event->title.c_str()); // then add the programme title
243 progTitle.setText(title); // sput this sring in our text box
244 length = event->description.length();
245 char* info = new char[length + 1]; // create programme detail string
246 strcpy(info, event->description.c_str());
247 progInfo.setText(info); // show programme detail string
248 // destroy dynamically allocated memory
255 // View::draw(); // draw pallet
257 fillColour(DrawStyle::TRANSPARENT);
260 // Moved all the dynamic data drawing to a seperate function
262 // Display the status and key stuff at the bottom
264 u4 keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
265 u4 keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
266 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
267 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
272 w.nextSymbol = WSymbol::LEFTARROW;
273 w.setPosition(keyx + 1, keyy + 20);
276 w.nextSymbol = WSymbol::UP;
277 w.setPosition(keyx + 26, keyy + 3);
280 w.nextSymbol = WSymbol::DOWN;
281 w.setPosition(keyx + 26, keyy + 36);
284 w.nextSymbol = WSymbol::RIGHTARROW;
285 w.setPosition(keyx + 50, keyy + 20);
288 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
290 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
291 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
293 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
294 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
296 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
297 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
299 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
300 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
302 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
303 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
305 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
306 DrawStyle red = DrawStyle(130, 0, 0);
307 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
309 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
310 w.nextSymbol = WSymbol::PLAY;
311 w.setPosition(keyx + 476, keyy + 5);
313 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
315 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
316 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
319 // Draw all the dynamic data
323 void VEpg::drawData()
325 // Not doing View::draw() every time causes
326 // things not to be cleared off the surface properly
327 // So, blank out the data area first
328 //int screenwidth=Video::getInstance()->getScreenWidth();
330 chanListbox.getRootBoxOffsetX(),
331 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
332 window_width * MINUTE_SCALE,
333 chanListbox.getHeight() + getFontHeight() + 4,
338 chanName.draw(); // TODO this should be dealt with by vvideolive
343 // Set timer to redraw to move the current time bar
347 if (dt == 0) dt = 60;
349 Timers::getInstance()->setTimerT(this, 1, dt);
352 void VEpg::timercall(int /*clientReference*/)
355 boxstack->update(this);
358 int VEpg::handleCommand(int command)
363 { // cursor up the channel list
366 boxstack->update(this);
367 return BoxStack::COMMAND_HANDLED;
370 { // cursor down the channel list
371 LogNT::getInstance()->debug(TAG, "Down start");
375 boxstack->update(this);
376 LogNT::getInstance()->debug(TAG, "Down end");
378 return BoxStack::COMMAND_HANDLED;
381 { // cursor left through time
382 selTime = thisEvent.time - 1;
384 boxstack->update(this);
385 return BoxStack::COMMAND_HANDLED;
389 // cursor right through time
390 selTime = thisEvent.time + thisEvent.duration;
392 boxstack->update(this);
393 return BoxStack::COMMAND_HANDLED;
397 // cursor up one page
398 chanListbox.pageUp();
400 boxstack->update(this);
401 return BoxStack::COMMAND_HANDLED;
405 // cursor down one page
406 chanListbox.pageDown();
408 boxstack->update(this);
409 return BoxStack::COMMAND_HANDLED;
413 // step forward 24 hours
414 selTime += 24 * 60 * 60;
416 boxstack->update(this);
417 return BoxStack::COMMAND_HANDLED;
421 // step forward 24 hours
422 selTime -= 24 * 60 * 60;
424 boxstack->update(this);
425 return BoxStack::COMMAND_HANDLED;
429 if (!chanList) return 2;
430 LogNT::getInstance()->debug(TAG, "ID {} TIME {} DURATION {} TITLE {}", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
431 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
434 boxstack->update(vs);
435 return BoxStack::COMMAND_HANDLED;
441 if (!chanList) return BoxStack::COMMAND_HANDLED;
442 // select programme and display menu TODO currently just changes to selected channel
444 currentChannelIndex = chanListbox.getCurrentOption();
448 Message* m = new Message(); // Must be done after this view deleted
451 m->message = Message::CHANNEL_CHANGE;
452 m->parameter = (*chanList)[currentChannelIndex]->number;
453 MessageQueue::getInstance()->postMessage(m);
458 if(command == Input::GO)
459 return BoxStack::COMMAND_HANDLED;
460 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
467 return BoxStack::DELETE_ME;
469 case Input::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);
488 return BoxStack::COMMAND_HANDLED;
490 case Input::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);
509 return BoxStack::COMMAND_HANDLED;
512 // stop command getting to any more views
513 return BoxStack::ABANDON_COMMAND;
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);
541 LOCALTIME_R(&t, &tms);
542 strftime(timeString, 19, "%a %d %b", &tms);
543 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
545 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
546 strftime(timeString, 19, "%H:%M", &tms);
547 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
549 rectangle(155, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
551 LOCALTIME_R(&t, &tms);
552 strftime(timeString, 19, "%H:%M", &tms);
553 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
554 rectangle(335, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
556 LOCALTIME_R(&t, &tms);
557 strftime(timeString, 19, "%H:%M", &tms);
558 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
559 rectangle(515, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
560 // pointer to selTime
561 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
565 if ((t >= ltime) && (t < (ltime + 9000)))
567 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
570 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
572 Event noevent; // an event to use if there are gaps in the epg
573 thisEvent.description = tr("There are no programme details available for this period");
574 thisEvent.duration = window_width * 60;
575 thisEvent.time = ltime;
576 thisEvent.title = tr("No programme details");
578 bool swapColour = false; // alternate cell colour
579 bool currentRow = false;
580 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
581 DrawStyle bg, fg; // background colour of cells in grid
582 // for each displayed channel, find programmes that fall in 2.5 hour time window
583 for(u4 listIndex = 0; listIndex < gridRows; listIndex++)
585 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
586 continue; // ensure nothing populates grid below last channel
588 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
589 noevent.time = ltime;
590 noevent.duration = window_width * 60;
592 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
595 thisEvent.description = tr("There are no programme details available for this period");
596 thisEvent.duration = window_width * 60;
597 thisEvent.time = ltime;
598 thisEvent.title = tr("No programme details");
601 if (eventLista[listIndex])
603 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
604 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
606 fg = DrawStyle::LIGHTTEXT;
607 event = (*eventLista[listIndex])[e];
610 u4 end = event->time + event->duration; // programme end time
611 if(event->time >= u4(ltime) + (window_width * 60)) // programme starts after RHS of window
612 continue; // that's enough of this channel's events
613 if(end <= u4(ltime)) // programme ends before LHS of window
614 continue; // this event is before the window - let's try the next event
615 // this event is one we are interested in
616 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
617 swapColour = !swapColour; // it wil be the other colour next time
618 if(event->time <= u4(selTime) && end > u4(selTime) && currentRow)
620 // this is the selected programme
621 thisEvent.description = event->description;
622 thisEvent.duration = event->duration;
623 thisEvent.time = event->time;
624 thisEvent.title = event->title;
625 thisEvent.id = event->id;
626 if(thisEvent.id == 0)
628 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
629 fg = DrawStyle::DARKTEXT;
633 if (currentRow && thisEvent.id == 0)
635 if (end <= u4(selTime) && end > u4(thisEvent.time))
636 thisEvent.time = end;
637 if (event->time > u4(selTime) && event->time < thisEvent.time + thisEvent.duration)
638 thisEvent.duration = event->time - thisEvent.time;
641 paintCell(event, y, bg, fg);
647 // no event list for this channel. Already painted noevent colour so just highlight if selected
650 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
651 fg = DrawStyle::DARKTEXT;
652 paintCell(&thisEvent, y, bg, fg);
656 bg = DrawStyle::NOPROGRAMME;
657 fg = DrawStyle::LIGHTTEXT;
658 noevent.title = tr("No programme details");
659 paintCell(&noevent, y, bg, fg);
662 y += getFontHeight() + 2;
667 void VEpg::updateEventList()
669 if (!chanList) return;
671 for(u4 listIndex = 0; listIndex < gridRows; listIndex++)
673 if(listTop + listIndex >= u4(chanListbox.getBottomOption()))
675 chan = (*chanList)[listTop + listIndex];
676 if (eventLista[listIndex])
678 (eventLista)[listIndex]->clear();
679 delete eventLista[listIndex];
681 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
685 void VEpg::setCurrentChannel()
687 chanName.setText((*chanList)[currentChannelIndex]->name);
690 chanName.getRootBoxRegion(&r);
691 boxstack->update(this, &r);
694 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
697 w = x = 0; // keep compiler happy
699 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
700 u4 end = event->time + event->duration; // programme end time
701 if(event->time <= u4(ltime) && end > u4(ltime)) // spans start of displayed window
703 x = 155; // LHS of window
704 if (end > (u4(ltime) + (window_width * 60)))
705 w = window_width * MINUTE_SCALE; // spans full 2 hour window
707 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
709 if((event->time >= u4(ltime)) && (event->time <= u4(ltime) + (window_width * 60))) // starts within window
711 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
712 w = MINUTE_SCALE * event->duration / 60;
713 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
714 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
716 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
717 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
718 rectangle(x, y, w, h, bg);
719 char* tt = new char[strlen(event->title.c_str()) + 1];
720 strcpy (tt, event->title.c_str());
722 unsigned int cur_length=1;
723 unsigned int text_max=strlen(tt);
725 if (Osd::getInstance()->charSet()!=1) mchar=true;
727 memset((void*)&state,0,sizeof(state));
730 for (textPos = 0; textPos <text_max; textPos+=cur_length)
734 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
735 if (cur_length <= 0){
738 } else cur_char= *(tt+textPos);
739 float thisCharWidth = charWidth(cur_char);
740 if (textWidth + thisCharWidth > w) // text will not fit in cell
744 textWidth += thisCharWidth;
746 char* tT = new char[textPos+1];
749 strncpy(tT, tt, textPos );
751 surface->drawText(tT, x+2, y, fg);
757 time_t VEpg::prevHour(time_t* t)
760 LOCALTIME_R(t, &tms);
766 void VEpg::processMessage(Message* m)
768 if (m->message == Message::MOUSE_MOVE)
770 if (chanListbox.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
773 boxstack->update(this);
776 else if (m->message == Message::MOUSE_LBDOWN)
778 if (chanListbox.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
780 Input::sendInputKey(Input::OK);
784 //check if press is outside this view! then simulate cancel
785 int x = m->parameter - getScreenX();
786 int y = m->tag - getScreenY();
787 int keyx = chanListbox.getRootBoxOffsetX();
788 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
790 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
792 Input::sendInputKey(Input::BACK);
794 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
796 boxstack->handleCommand(Input::RED);
798 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
800 boxstack->handleCommand(Input::GREEN);
802 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
804 boxstack->handleCommand(Input::YELLOW);
806 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
808 boxstack->handleCommand(Input::BLUE);
810 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
812 Input::sendInputKey(Input::BACK);
814 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
816 boxstack->handleCommand(Input::RECORD);
818 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
820 boxstack->handleCommand(Input::PLAY);
822 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
824 boxstack->handleCommand(Input::GO);
826 else if ( x>=(chanListbox.getRootBoxOffsetX())
827 && y>=(chanListbox.getRootBoxOffsetY() + 5)
828 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
829 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
830 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
833 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
834 int row=cy/(getFontHeight()+2);
835 int clistTop = chanListbox.getTopOption();
836 chanListbox.hintSetCurrent(clistTop+row);
838 time_t ttime = cx*60/MINUTE_SCALE+ltime;
839 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
843 boxstack->update(this);