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"
49 static const char* TAG = "VEpg";
51 VEpg* VEpg::instance = NULL;
53 VEpg::VEpg(MessageReceiver* 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 // setBackgroundColour(DrawStyle::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;
185 MessageQueue::getInstance()->addReceiver(this);
188 void VEpg::preDelete()
190 Timers::getInstance()->cancelTimer(this, 1);
195 MessageQueue::getInstance()->removeReceiver(this);
199 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
201 if (eventLista[listIndex])
203 (eventLista)[listIndex]->clear();
204 delete eventLista[listIndex];
207 // delete [] eventLista; // FIXME
209 // destroy dynamically allocated memory
212 VEpg* VEpg::getInstance()
217 void VEpg::setInfo(Event* event)
220 struct tm btime; // to hold programme start and end time
221 char timeString[9]; // to hold programme start and end time
222 int length = strlen(event->title.c_str()); // calculate length of programme title string
223 char* title = new char[length + 15]; // create string to hold start time, end time and programme title
224 time_t eventtime = event->time;
225 LOCALTIME_R(&eventtime, &btime); //get programme start time
227 strftime(timeString, 9, "%0H:%0M - ", &btime); // and format it as hh:mm -
229 strftime(timeString, 9, "%H:%M - ", &btime); // and format it as hh:mm -
231 strcpy(title, timeString); // put it in our buffer
232 t = event->time + event->duration; //get programme end time
233 LOCALTIME_R(&t, &btime);
235 strftime(timeString, 7, "%0H:%0M ", &btime); // and format it as hh:mm -
237 strftime(timeString, 7, "%H:%M ", &btime); // and format it as hh:mm -
239 strcat(title, timeString); // put it in our buffer
241 strcat(title, event->title.c_str()); // then add the programme title
242 progTitle.setText(title); // sput this sring in our text box
243 length = event->description.length();
244 char* info = new char[length + 1]; // create programme detail string
245 strcpy(info, event->description.c_str());
246 progInfo.setText(info); // show programme detail string
247 // destroy dynamically allocated memory
254 // View::draw(); // draw pallet
256 fillColour(DrawStyle::TRANSPARENT);
259 // Moved all the dynamic data drawing to a seperate function
261 // Display the status and key stuff at the bottom
263 UINT keyx = chanListbox.getRootBoxOffsetX() + chanListbox.getColumn(0);
264 UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
265 rectangle (0, keyy, Video::getInstance()->getScreenWidth() ,
266 Video::getInstance()->getScreenHeight()-keyy, DrawStyle::DARKGREY);
271 w.nextSymbol = WSymbol::LEFTARROW;
272 w.setPosition(keyx + 1, keyy + 20);
275 w.nextSymbol = WSymbol::UP;
276 w.setPosition(keyx + 26, keyy + 3);
279 w.nextSymbol = WSymbol::DOWN;
280 w.setPosition(keyx + 26, keyy + 36);
283 w.nextSymbol = WSymbol::RIGHTARROW;
284 w.setPosition(keyx + 50, keyy + 20);
287 drawTextCentre(tr("OK"), keyx + 35, keyy + 20, DrawStyle::LIGHTTEXT);
289 rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, DrawStyle::RED);
290 drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
292 rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::GREEN);
293 drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
295 rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, DrawStyle::YELLOW);
296 drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
298 rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, DrawStyle::BLUE);
299 drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
301 rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, DrawStyle::GREY);
302 drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
304 rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, DrawStyle::GREY);
305 DrawStyle red = DrawStyle(130, 0, 0);
306 drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
308 rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, DrawStyle::GREY);
309 w.nextSymbol = WSymbol::PLAY;
310 w.setPosition(keyx + 476, keyy + 5);
312 drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);
314 rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, DrawStyle::GREY);
315 drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
318 // Draw all the dynamic data
322 void VEpg::drawData()
324 // Not doing View::draw() every time causes
325 // things not to be cleared off the surface properly
326 // So, blank out the data area first
327 //int screenwidth=Video::getInstance()->getScreenWidth();
329 chanListbox.getRootBoxOffsetX(),
330 chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,
331 window_width * MINUTE_SCALE,
332 chanListbox.getHeight() + getFontHeight() + 4,
337 chanName.draw(); // TODO this should be dealt with by vvideolive
342 // Set timer to redraw to move the current time bar
346 if (dt == 0) dt = 60;
348 Timers::getInstance()->setTimerT(this, 1, dt);
351 void VEpg::timercall(int /*clientReference*/)
354 boxstack->update(this);
357 int VEpg::handleCommand(int command)
362 { // cursor up the channel list
365 boxstack->update(this);
366 return BoxStack::COMMAND_HANDLED;
369 { // cursor down the channel list
370 LogNT::getInstance()->debug(TAG, "Down start");
374 boxstack->update(this);
375 LogNT::getInstance()->debug(TAG, "Down end");
377 return BoxStack::COMMAND_HANDLED;
380 { // cursor left through time
381 selTime = thisEvent.time - 1;
383 boxstack->update(this);
384 return BoxStack::COMMAND_HANDLED;
388 // cursor right through time
389 selTime = thisEvent.time + thisEvent.duration;
391 boxstack->update(this);
392 return BoxStack::COMMAND_HANDLED;
396 // cursor up one page
397 chanListbox.pageUp();
399 boxstack->update(this);
400 return BoxStack::COMMAND_HANDLED;
404 // cursor down one page
405 chanListbox.pageDown();
407 boxstack->update(this);
408 return BoxStack::COMMAND_HANDLED;
412 // step forward 24 hours
413 selTime += 24 * 60 * 60;
415 boxstack->update(this);
416 return BoxStack::COMMAND_HANDLED;
420 // step forward 24 hours
421 selTime -= 24 * 60 * 60;
423 boxstack->update(this);
424 return BoxStack::COMMAND_HANDLED;
428 if (!chanList) return 2;
429 LogNT::getInstance()->debug(TAG, "ID {} TIME {} DURATION {} TITLE {}", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);
430 VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);
433 boxstack->update(vs);
434 return BoxStack::COMMAND_HANDLED;
440 if (!chanList) return BoxStack::COMMAND_HANDLED;
441 // select programme and display menu TODO currently just changes to selected channel
443 currentChannelIndex = chanListbox.getCurrentOption();
447 Message* m = new Message(); // Must be done after this view deleted
450 m->message = Message::CHANNEL_CHANGE;
451 m->parameter = (*chanList)[currentChannelIndex]->number;
452 MessageQueue::getInstance()->postMessage(m);
457 if(command == Input::GO)
458 return BoxStack::COMMAND_HANDLED;
459 // GO just changes channel in preview, PLAY changes channel and returns to normal TV
464 return BoxStack::DELETE_ME;
466 case Input::CHANNELUP:
468 if (currentChannelIndex == (chanList->size() - 1)) // at the end
469 currentChannelIndex = 0;
471 ++currentChannelIndex;
475 Message* m = new Message(); // Must be done after this view deleted
478 m->message = Message::CHANNEL_CHANGE;
479 m->parameter = (*chanList)[currentChannelIndex]->number;
480 MessageQueue::getInstance()->postMessage(m);
485 return BoxStack::COMMAND_HANDLED;
487 case Input::CHANNELDOWN:
489 if (currentChannelIndex == 0) // at the start
490 currentChannelIndex = chanList->size() - 1; // so go to end
492 --currentChannelIndex;
496 Message* m = new Message(); // Must be done after this view deleted
499 m->message = Message::CHANNEL_CHANGE;
500 m->parameter = (*chanList)[currentChannelIndex]->number;
501 MessageQueue::getInstance()->postMessage(m);
506 return BoxStack::COMMAND_HANDLED;
509 // stop command getting to any more views
510 return BoxStack::ABANDON_COMMAND;
513 void VEpg::drawgrid() // redraws grid and select programme
515 // draw the grid of programmes
518 time(&t); // set t = now
520 selTime = t; // don't allow cursor in the past
521 if(listTop != chanListbox.getTopOption())
523 // chanListbox has scrolled TODO speed up by changing only rows that have changed
524 listTop = chanListbox.getTopOption();
527 if ((selTime >= ltime + (int)window_width * 60) || (selTime <= ltime))
529 // we have cursored back before left time of window
530 //TODO check that this and above don't happen together
531 ltime = prevHour(&selTime);
538 LOCALTIME_R(&t, &tms);
539 strftime(timeString, 19, "%a %d %b", &tms);
540 int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;
542 drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date
543 strftime(timeString, 19, "%H:%M", &tms);
544 drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time
546 rectangle(155, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
548 LOCALTIME_R(&t, &tms);
549 strftime(timeString, 19, "%H:%M", &tms);
550 drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time
551 rectangle(335, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
553 LOCALTIME_R(&t, &tms);
554 strftime(timeString, 19, "%H:%M", &tms);
555 drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time
556 rectangle(515, timey + getFontHeight(), 2, 7, DrawStyle::WHITE);
557 // pointer to selTime
558 //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));
562 if ((t >= ltime) && (t < (ltime + 9000)))
564 rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * gridRows) + 7 + 2, DrawStyle::RED);
567 // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?
569 Event noevent; // an event to use if there are gaps in the epg
570 thisEvent.description = tr("There are no programme details available for this period");
571 thisEvent.duration = window_width * 60;
572 thisEvent.time = ltime;
573 thisEvent.title = tr("No programme details");
575 bool swapColour = false; // alternate cell colour
576 bool currentRow = false;
577 int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell
578 DrawStyle bg, fg; // background colour of cells in grid
579 // for each displayed channel, find programmes that fall in 2.5 hour time window
580 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
582 if (listTop + (int)listIndex >= chanListbox.getBottomOption())
583 continue; // ensure nothing populates grid below last channel
585 currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());
586 noevent.time = ltime;
587 noevent.duration = window_width * 60;
589 paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes
592 thisEvent.description = tr("There are no programme details available for this period");
593 thisEvent.duration = window_width * 60;
594 thisEvent.time = ltime;
595 thisEvent.title = tr("No programme details");
598 if (eventLista[listIndex])
600 sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());
601 for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel
603 fg = DrawStyle::LIGHTTEXT;
604 event = (*eventLista[listIndex])[e];
607 UINT end = event->time + event->duration; // programme end time
608 if(event->time >= UINT(ltime) + (window_width * 60)) // programme starts after RHS of window
609 continue; // that's enough of this channel's events
610 if(end <= UINT(ltime)) // programme ends before LHS of window
611 continue; // this event is before the window - let's try the next event
612 // this event is one we are interested in
613 bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour
614 swapColour = !swapColour; // it wil be the other colour next time
615 if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)
617 // this is the selected programme
618 thisEvent.description = event->description;
619 thisEvent.duration = event->duration;
620 thisEvent.time = event->time;
621 thisEvent.title = event->title;
622 thisEvent.id = event->id;
623 if(thisEvent.id == 0)
625 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
626 fg = DrawStyle::DARKTEXT;
630 if (currentRow && thisEvent.id == 0)
632 if (end <= UINT(selTime) && end > UINT(thisEvent.time))
633 thisEvent.time = end;
634 if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)
635 thisEvent.duration = event->time - thisEvent.time;
638 paintCell(event, y, bg, fg);
644 // no event list for this channel. Already painted noevent colour so just highlight if selected
647 bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell
648 fg = DrawStyle::DARKTEXT;
649 paintCell(&thisEvent, y, bg, fg);
653 bg = DrawStyle::NOPROGRAMME;
654 fg = DrawStyle::LIGHTTEXT;
655 noevent.title = tr("No programme details");
656 paintCell(&noevent, y, bg, fg);
659 y += getFontHeight() + 2;
664 void VEpg::updateEventList()
666 if (!chanList) return;
668 for(UINT listIndex = 0; listIndex < gridRows; listIndex++)
670 if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))
672 chan = (*chanList)[listTop + listIndex];
673 if (eventLista[listIndex])
675 (eventLista)[listIndex]->clear();
676 delete eventLista[listIndex];
678 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
682 void VEpg::setCurrentChannel()
684 chanName.setText((*chanList)[currentChannelIndex]->name);
687 chanName.getRootBoxRegion(&r);
688 boxstack->update(this, &r);
691 void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)
694 w = x = 0; // keep compiler happy
696 h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height
697 UINT end = event->time + event->duration; // programme end time
698 if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window
700 x = 155; // LHS of window
701 if (end > (UINT(ltime) + (window_width * 60)))
702 w = window_width * MINUTE_SCALE; // spans full 2 hour window
704 w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme
706 if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (window_width * 60))) // starts within window
708 x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
709 w = MINUTE_SCALE * event->duration / 60;
710 //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)
711 // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window
713 if (w > 155 + (int)window_width * MINUTE_SCALE - x)
714 w = 155 + window_width * MINUTE_SCALE -x; // limit cells to RHS of window
715 rectangle(x, y, w, h, bg);
716 char* tt = new char[strlen(event->title.c_str()) + 1];
717 strcpy (tt, event->title.c_str());
719 unsigned int cur_length=1;
720 unsigned int text_max=strlen(tt);
722 if (Osd::getInstance()->charSet()!=1) mchar=true;
724 memset((void*)&state,0,sizeof(state));
727 for (textPos = 0; textPos <text_max; textPos+=cur_length)
731 cur_length = mbrtowc(&cur_char, tt + textPos, text_max-textPos, &state);
732 if (cur_length <= 0){
735 } else cur_char= *(tt+textPos);
736 float thisCharWidth = charWidth(cur_char);
737 if (textWidth + thisCharWidth > w) // text will not fit in cell
741 textWidth += thisCharWidth;
743 char* tT = new char[textPos+1];
746 strncpy(tT, tt, textPos );
748 surface->drawText(tT, x+2, y, fg);
754 time_t VEpg::prevHour(time_t* t)
757 LOCALTIME_R(t, &tms);
763 void VEpg::processMessage(Message* m)
765 if (m->message == Message::MOUSE_MOVE)
767 if (chanListbox.mouseMove(m->parameter - getScreenX(), m->tag - getScreenY()))
770 boxstack->update(this);
773 else if (m->message == Message::MOUSE_LBDOWN)
775 if (chanListbox.mouseLBDOWN(m->parameter - getScreenX(), m->tag - getScreenY()))
777 Input::sendInputKey(Input::OK);
781 //check if press is outside this view! then simulate cancel
782 int x = m->parameter - getScreenX();
783 int y = m->tag - getScreenY();
784 int keyx = chanListbox.getRootBoxOffsetX();
785 int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;
787 if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())
789 Input::sendInputKey(Input::BACK);
791 else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))
793 boxstack->handleCommand(Input::RED);
795 else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))
797 boxstack->handleCommand(Input::GREEN);
799 else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))
801 boxstack->handleCommand(Input::YELLOW);
803 else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))
805 boxstack->handleCommand(Input::BLUE);
807 else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))
809 Input::sendInputKey(Input::BACK);
811 else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))
813 boxstack->handleCommand(Input::RECORD);
815 else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))
817 boxstack->handleCommand(Input::PLAY);
819 else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))
821 boxstack->handleCommand(Input::GO);
823 else if ( x>=(chanListbox.getRootBoxOffsetX())
824 && y>=(chanListbox.getRootBoxOffsetY() + 5)
825 // &&x<=(chanListbox.getOffsetX()+155 + window_width * MINUTE_SCALE)
826 &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()
827 - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)
830 int cy=y-(chanListbox.getRootBoxOffsetY() + 5);
831 int row=cy/(getFontHeight()+2);
832 int clistTop = chanListbox.getTopOption();
833 chanListbox.hintSetCurrent(clistTop+row);
835 time_t ttime = cx*60/MINUTE_SCALE+ltime;
836 //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);
840 boxstack->update(this);